package models

import (
	"encoding/json"
	"github.com/XieWeikai/PlaceToGo/utils"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"html"
	"strings"
)

// user type
const (
	ADMIN = "admin"
	USER  = "default"
)

// user level
const (
	General = ""
	VIP     = "VIP"
)

type Address struct {
	City     string `json:"city" binding:"required"`
	Province string `json:"province" binding:"required"`
	Country  string `json:"country" binding:"required"`
}

type User struct {
	gorm.Model
	Username string `gorm:"size:255; not null; unique" json:"username" binding:"required"`
	Password string `gorm:"size:255; not null" json:"password" binding:"required"`

	UserType       string  `gorm:"size:255; not null" json:"userType"`                // "admin" or "user"
	Name           string  `gorm:"size:255; not null" json:"name" binding:"required"` // real name
	IdentityType   string  `gorm:"not null" json:"idType" binding:"required"`
	IdentityNumber string  `gorm:"unique;not null" json:"idNumber" binding:"required"`
	Phone          string  `gorm:"size:255; not null" json:"phone" binding:"required"`
	UserIntro      string  `gorm:"type:varchar(512)" json:"userProfile" binding:"required"`
	UserLevel      string  `gorm:"type:varchar(20);not null" json:"userLevel"`
	Address        Address `gorm:"embedded" json:"address" binding:"required"`
}

type UpdateUserDTO struct {
	Phone     string `json:"phone"`
	UserIntro string `json:"userProfile"`
	Password  string `json:"password"`
}

func (u *User) Save() (*User, error) {
	if e := DB.Create(u).Error; e != nil {
		return nil, e
	}
	return u, nil
}

func (u *User) UpdateById(m *UpdateUserDTO) error {
	if m.Password != "" {
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(m.Password), bcrypt.DefaultCost)
		if err != nil {
			return err
		}
		m.Password = string(hashedPassword)
	}

	return DB.Model(u).Updates(m).Error
}

func (u *User) GetFirst(columns ...string) (*User, error) {
	query := DB.Where(u)
	if len(columns) > 0 {
		query = query.Select(columns)
	}
	e := query.First(u).Error
	if e != nil {
		return nil, e
	}
	return u, nil
}

func (u *User) GetUsers(limit int, offset int, sortBy string, sortOrder string, columns ...string) ([]User, error) {
	var users []User
	query := DB.Where(u)

	// 如果提供了有效的排序字段和顺序，则应用它们
	if sortBy != "" {
		query = query.Order(sortBy + " " + sortOrder)
	}

	// 应用分页设置
	query = query.Limit(limit).Offset(offset)

	if len(columns) > 0 {
		query = query.Select(columns)
	}

	// 注意：传递users的指针到Find()方法中
	if err := query.Find(&users).Error; err != nil {
		return nil, err
	}

	return users, nil
}

func GetUsers(filter map[string]any, limit int, offset int, sortBy string, sortOrder string, columns ...string) ([]User, int64, error) {
	var users []User

	query := DB.Model(&User{})

	if q, ok := filter["q"]; ok {
		// 如果存在 q 参数，将其从 filter 中删除
		delete(filter, "q")

		// 将查询字符串转换为 GORM 查询
		qs, args := utils.SearchQuery(q.(string), "username", "user_type", "name", "identity_type",
			"identity_number", "phone", "user_intro", "user_level", "city", "province", "country")

		//log.Println("qs: ", qs, "args: ", args)
		query = query.Where(qs, args...)
	}

	query = query.Where(filter)

	// 如果提供了有效的排序字段和顺序，则应用它们
	if sortBy != "" {
		query = query.Order(sortBy + " " + sortOrder)
	}

	var cnt int64

	if err := query.Count(&cnt).Error; err != nil {
		return nil, 0, err
	}

	// 应用分页设置
	query = query.Limit(limit).Offset(offset)

	if len(columns) > 0 {
		query = query.Select(columns)
	}

	// 注意：传递users的指针到Find()方法中
	if err := query.Find(&users).Error; err != nil {
		return nil, 0, err
	}

	return users, cnt, nil
}

func (u *User) MarshalJSON() ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"id":          u.ID,
		"name":        u.Name,
		"username":    u.Username,
		"userType":    u.UserType,
		"idType":      u.IdentityType,
		"idNumber":    u.IdentityNumber,
		"phone":       u.Phone,
		"userProfile": u.UserIntro,
		"userLevel":   u.UserLevel,
		"ctime":       u.CreatedAt,
		"utime":       u.UpdatedAt,
		"address":     u.Address,
	})
}

func (u *User) BeforeSave(tx *gorm.DB) error {
	// turn password into hash
	// 这里已经随机加盐了，hashedPassword里有盐值和hash值
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	u.Password = string(hashedPassword)

	//remove spaces in username
	u.Username = html.EscapeString(strings.TrimSpace(u.Username))

	return nil
}
